package com.gitee.apanlh.util.reflection;

import com.gitee.apanlh.exp.MethodInvokeException;
import com.gitee.apanlh.spring.BeanContextUtils;
import com.gitee.apanlh.util.base.ArrayUtils;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.ValidParam;
/**	
 * 	{@code
 * 		MethodInvokeBuilder.builder(Abc.class, "abc").setParamTypes(1).setCallParams(1, "99").build();
 * 	}
 * 	<p>
 * 	invoke建造
 * 	@author Pan
 */
public class MethodInvokeBuilder {
	
	/** 调用类 */
	private Class<?> clazz;
	/** 实例对象 */
	private Object obj;
	/** 调用参数 */
	private Object[] callParams;
	/** 调用参数类型 */
	private Class<?>[] callParamTypes;
	/** 调用方法 */
	private String callMethod;
	/** 是否为SpringBean */
	private boolean isSpringBean;
	
	/**
	 * 	默认构造函数-私有
	 * 	@author Pan
	 */
	private MethodInvokeBuilder() {
		super();
	}
	
	/**
	 * 	构造函数
	 * 	<br>非SpringBean创建
	 * 
	 * 	@author Pan
	 * 	@param 	clazz		类
	 * 	@param 	bean		对象
	 * 	@param 	method		方法
	 */
	private <T> MethodInvokeBuilder(Class<?> clazz, T bean, String method) {
		this(clazz, bean, method, false);
	}
	
	/**
	 * 	构造函数
	 * 	<br>自定义类
	 * 	<br>自定义对象
	 * 	<br>自定义方法
	 * 	<br>是否为SpringBean
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz				类
	 * 	@param 	bean				对象
	 * 	@param 	methodName			方法名称
	 * 	@param 	isSpringBean		true为SpringBean
	 */
	private <T> MethodInvokeBuilder(Class<?> clazz, T bean, String methodName, boolean isSpringBean) {
		this.clazz = clazz;
		this.obj = bean;
		this.callMethod = methodName;
		this.isSpringBean = isSpringBean;
	}
	
	/**	
	 * 	设置方法
	 * 	
	 * 	@author Pan
	 * 	@param 	method	执行方法
	 * 	@return	MethodInvokeBuilder
	 */
	protected MethodInvokeBuilder setMethod(String method) {
		this.callMethod = method;
		return this;
	}
	
	/**	
	 * 	设置参数值
	 * 	<br>与形参类型一一对应
	 * 
	 * 	@author Pan
	 * 	@param 	args  参数
	 * 	@return	MethodInvokeBuilder
	 */
	public MethodInvokeBuilder setCallParams(Object... args) {
		this.callParams = args;
		return this;
	}
	
	/**	
	 * 	设置形参类型
	 * 	<br>与参数值类型一一对应
	 * 
	 * 	@author Pan
	 * 	@param 	parameterTypes	形参类型
	 * 	@return	MethodInvokeBuilder
	 */
	public MethodInvokeBuilder setParamTypes(Class<?>... parameterTypes) {
		this.callParamTypes = parameterTypes;
		return this;
	}
	
	/**	
	 * 	设置类
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz 类
	 * 	@return	MethodInvokeBuilder
	 */
	protected MethodInvokeBuilder setClazz(Class<?> clazz) {
		this.clazz = clazz;
		return this;
	}
	
	/**		
	 * 	获取对象
	 * 
	 * 	@author Pan
	 * 	@return	Object
	 */
	protected Object getObject() {
		return this.obj;
	}
	
	/**	
	 * 	获取方法
	 * 
	 * 	@author Pan
	 * 	@return	String
	 */
	protected String getMethod() {
		return this.callMethod;
	}
	
	/**	
	 * 	获取参数类型
	 * 	
	 * 	@author Pan
	 * 	@return	Class[]
	 */
	protected Class<?>[] getParamTypes() {
		return this.callParamTypes;
	}

	/**	
	 * 	获取调用参数
	 * 	
	 * 	@author Pan
	 * 	@return	Object[]
	 */
	protected Object[] getCallParams() {
		return this.callParams;
	}
	
	/**	
	 * 	获取类
	 * 	
	 * 	@author Pan
	 * 	@return	Class
	 */
	protected Class<?> getClazz() {
		return this.clazz;
	}
	
	/**	
	 * 	获取SpringBean名字 默认开头小写
	 * 	
	 * 	@author Pan
	 * 	@return	Object
	 */
	private Object newInstance() {
		//	非静态时
		if (this.obj == null && !ReflectionUtils.isStaticMethod(this.clazz, this.callMethod) && !isSpringBean) {
			this.obj = ReflectionUtils.newInstance(clazz);
		}
		
		//	获取SpringBean
		if (isSpringBean) {
			String className = ClassUtils.getSimpleName(getClazz(), true);
			Object bean = BeanContextUtils.getBean(className);
			Assert.isNotNull(bean, StringUtils.format("cause spring bean is null, beanName[{}]", className));
			this.obj = bean;
		} 
		return this.obj;
	}
	
	/**
	 * 	构建时执行invoke方法
	 * 	
	 * 	@author Pan
	 * 	@return	Object
	 */
	public Object build() {
		validParamType();
		try {
			return this.clazz.getMethod(this.callMethod, this.callParamTypes).invoke(newInstance(), this.callParams);
		} catch (Exception e) {
			throw new MethodInvokeException(e.getMessage(), e);
		}
	}
	
	/**
	 * 	构建时执行invoke方法
	 * 	<br>返回指定类型
	 * 	
	 * 	@author Pan
	 * 	@param  <T>     数据类型
	 * 	@param 	clazz	返回类型
	 * 	@return	T
	 */
	@SuppressWarnings("unchecked")
	public <T> T build(Class<T> clazz) {
		return (T) build();
	}
	
	/**	
	 * 	校验调用参数
	 * 	<br>参数类型校验通过invoke方法校验
	 * 
	 * 	@author Pan
	 */
	private void validParamType() {
		Assert.isNotNull(this.clazz, "invoke class is null");
		Assert.isNotNull(this.callMethod, "invoke callMethod is null");
		
		
		//	默认构造参数
		if (ValidParam.isEmpty(this.callParamTypes) && ValidParam.isEmpty(this.callParams)) {
			return ;
		}
		if (this.callParamTypes == null || this.callParams == null) {
			throw new MethodInvokeException("参数类型(callParamTypes)未设置或调用参数(callParam)未设置");
		}
		if (this.callParamTypes.length != this.callParams.length) {
			throw new MethodInvokeException("参数类型(callParamTypes)长度与调用参数(callParam)长度不一致");
		}
	}
	
	@Override
	public String toString() {
		return StringUtils.format("MethodInvokeBuilder [clazz={}, obj={}, callParams={}, callParamTypes={}, callMethod={}]",
			clazz, 
			obj, 
			ArrayUtils.toString(callParams), 
			ArrayUtils.toString(callParamTypes), 
			callMethod
		);
	}

	/**	
	 * 	builder 创建
	 * 	需要set(参数类型、调用参数)
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz	类
	 * 	@param 	method	方法名
	 * 	@return	MethodInvokeBuilder
	 */
	public static MethodInvokeBuilder builder(Class<?> clazz, String method) {
		return new MethodInvokeBuilder(clazz, null, method);
	}

	/**	
	 * 	builder 创建
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      	数据类型
	 * 	@param 	bean		已实例的对象
	 * 	@param 	method		方法名
	 * 	@return	MethodInvokeBuilder
	 */
	public static <T> MethodInvokeBuilder builder(T bean, String method) {
		Assert.isNotNull(bean);
		
		return new MethodInvokeBuilder(bean.getClass(), bean, method);
	}
	
	/**	
	 * 	builder 创建
	 * 	<br>需要set(参数类型、调用参数)
	 * 	<br>SpringBean模式
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz		类
	 * 	@param 	method		方法名
	 * 	@return	MethodInvokeBuilder
	 */
	public static MethodInvokeBuilder builderSpringBean(Class<?> clazz, String method) {
		return new MethodInvokeBuilder(clazz, null, method, true);
	}
}
